<!-- src/components/Upload/BaseUpload.vue -->
<template>
  <div class="base-upload">
    <el-upload
      v-bind="$attrs"
      :multiple="multiple"
      :accept="accept"
      :limit="maxCount"
      :file-list="modelValue"
      :on-exceed="handleExceed"
      :before-upload="beforeUpload"
      :on-success="handleSuccess"
      :on-error="handleError"
      :on-remove="handleRemove"
      :http-request="customRequest"
      :list-type="listType"
    >
      <slot>
        <el-button type="primary">点击上传</el-button>
      </slot>

      <template #tip>
        <div class="el-upload__tip" v-if="tip">{{ tip }}</div>
      </template>
    </el-upload>

    <el-progress
      v-if="uploading"
      :percentage="progress"
      :status="uploadStatus"
      class="mt-2"
    />
  </div>
</template>

<script lang="ts" setup>
import { ref, computed, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { Uploader } from '../utils/uploader';
import { validateFile, validateImageDimensions, validateFileType, validateFileSize } from '../utils/validator';
import type { UploadFile, UploadFiles, UploadRequestOptions } from 'element-plus';

interface Props {
  modelValue: string | string[] | undefined;
  maxCount?: number;
  maxSize?: number;
  accept?: string;
  tip?: string;
  endpoint?: string;
  listType?: 'text' | 'picture' | 'picture-card';
  autoUpload?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  maxCount: 1,
  maxSize: 2,
  accept: 'image/*',
  endpoint: '/upload',
  listType: 'text',
  autoUpload: true
});

const emit = defineEmits<{
  (e: 'update:modelValue', value: string | string[]): void;
  (e: 'success', payload: { url: string; file: UploadFile; fileList: UploadFiles }): void;
  (e: 'error', error: Error): void;
  (e: 'progress', progress: number): void;
}>();

const uploading = ref(false);
const progress = ref(0);
const uploadStatus = ref<'success' | 'exception' | null>(null);
const uploader = new Uploader({
  endpoint: props.endpoint,
  maxSizeMB: props.maxSize
});

const multiple = computed(() => props.maxCount > 1);

const customRequest = async (options: UploadRequestOptions) => {
  try {
    uploading.value = true;
    progress.value = 0;
    uploadStatus.value = null;

    const onProgress: (progress: number) => void = (p) => {
      progress.value = p;
      emit('progress', p);
    };

    const response = await uploader.upload(options.file, {
      allowedTypes: props.accept.split(',').map(t => t.trim()),
      onProgress
    });

    uploadStatus.value = 'success';
    return response;
  } catch (error) {
    uploadStatus.value = 'exception';
    throw error;
  } finally {
    uploading.value = false;
  }
};

const beforeUpload = async (file: File) => {
  try {
    const validation = await validateFile(file, {
      allowedTypes: props.accept.split(',').map(t => t.trim()),
      maxSizeMB: props.maxSize
    });

    if (!validation.valid) {
      throw new Error(validation.message);
    }

    return true;
  } catch (error: any) {
    ElMessage.error(error.message);
    return false;
  }
};

const handleSuccess = (response: any, file: UploadFile, fileList: UploadFiles) => {
  const url = response.url || response.data?.url;
  if (!url) return;

  const newValue = props.maxCount === 1
    ? url
    : [...(Array.isArray(props.modelValue) ? props.modelValue : []), url].slice(0, props.maxCount);

  emit('update:modelValue', newValue);
  emit('success', { url, file, fileList });
};

const handleError = (error: Error) => {
  ElMessage.error(`上传失败: ${error.message}`);
  emit('error', error);
};

const handleRemove = (file: UploadFile, fileList: UploadFiles) => {
  emit('update:modelValue', props.maxCount === 1 ? '' : fileList.map(f => f.url!));
};

const handleExceed = () => {
  ElMessage.warning(`最多上传 ${props.maxCount} 个文件`);
};
</script>

<style scoped lang="scss">
.base-upload {
  :deep(.el-upload) {
    width: 100%;
  }

  :deep(.el-upload-list__item) {
    transition: all 0.3s;
  }
}
</style>
